﻿using System;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.HighLevel
{
    /// <summary>
    /// Reference: http://stackoverflow.com/questions/8079526/lowpass-and-high-pass-filter-in-c-sharp
    /// </summary>
    public abstract class BasePassModule : BaseModule
    {
        private double mLastFrequency;
        private double mLastResonance;

        /// <summary>
        /// rez amount, from sqrt(2) to ~ 0.1
        /// </summary>
        protected double resonance;
        protected double frequency;
        protected int sampleRate;
        protected double c, a1, a2, a3, b1, b2;

        /// <summary>
        /// Array of input values, latest are in front
        /// </summary>
        protected double[] inputHistory = new double[2];

        /// <summary>
        /// Array of output values, latest are in front
        /// </summary>
        protected double[] outputHistory = new double[3];

        public readonly ModuleInput Input;
        public readonly ModuleOutput Output;
        public readonly InputModuleParameter Frequency;
        public readonly InputModuleParameter Resonance;

        public BasePassModule(ModuleManager manager)
            : base(manager)
        {
            AddInput(Input = new ModuleInput("Input", IOType.AudioOrWaveform, this));
            AddOutput(Output = new ModuleOutput("Output", IOType.AudioOrWaveform, this));

            Frequency = new InputModuleParameter(this, "Freq", 0, IOType.AudioOrWaveform);
            Frequency.Label = "Frequency";
            Frequency.MinValue = 10;
            Frequency.MaxValue = 21000;
            Frequency.RawValue = 6000;
            AddModuleParameter(Frequency);

            Resonance = new InputModuleParameter(this, "Res", 0, IOType.AudioOrWaveform);
            Resonance.Label = "Resonance";
            Resonance.MinValue = 0.1;
            Resonance.MaxValue = Math.Sqrt(2);
            Resonance.RawValue = 0.3;
            AddModuleParameter(Resonance);
        }

        public override void ChangeSampleRate(double sampleRate)
        {
            InitializeLowPass(Frequency.FirstValue, (int)sampleRate, Resonance.FirstValue);
        }

        protected abstract void InitializeLowPass(double frequency, int sampleRate, double resonance);

        public override void Process()
        {
            if (Input.IsConnected)
            {
                for (int i = 0; i < Input.FromOutput.AudioBuffer.Length; i++)
                {
                    double newInput = Input.FromOutput.AudioBuffer[i];
                    double freq = Frequency.Values[i];
                    double res = Resonance.Values[i];

                    if (freq != mLastFrequency || res != mLastResonance)
                    {
                        InitializeLowPass(freq, sampleRate, res);

                        mLastFrequency = freq;
                        mLastResonance = res;
                    }

                    double newOutput = a1 * newInput + a2 * this.inputHistory[0] + a3 * this.inputHistory[1] - b1 * this.outputHistory[0] - b2 * this.outputHistory[1];

                    this.inputHistory[1] = this.inputHistory[0];
                    this.inputHistory[0] = newInput;

                    this.outputHistory[2] = this.outputHistory[1];
                    this.outputHistory[1] = this.outputHistory[0];
                    this.outputHistory[0] = newOutput;

                    Output.AudioBuffer[i] = newOutput;
                }
            }
        }
    }
}
